#include "mmt_MP_engine.h"
#include "Message.h"
#include "Constants.h"

//------------------------------------------------------------>
mmt_MP_engine::mmt_MP_engine(void) 
:	_isGameStarted(false),
	_ipInputLabel(0),
	_coordinateSystemSceneNode(0),
	_level(0),
	_controlMessage(0),
	_myPlayer(0)
{
	Object::setMessageHandler( this );
}

//------------------------------------------------------------>
mmt_MP_engine::~mmt_MP_engine(void)
{
	//Object::stopReceiving();
}

//------------------------------------------------------------>

void mmt_MP_engine::createScene(void)
{
	// Set ambient light
	mSceneMgr->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));

	// Create a light
	Ogre::Light* l = mSceneMgr->createLight("MainLight");
	l->setPosition(20,80,50);
	createGui();
	createCoordinateSystem();
}
//------------------------------------------------------------>

bool mmt_MP_engine::frameEnded( const Ogre::FrameEvent& evt )
{
	return BaseApplication::frameEnded( evt );
}

//------------------------------------------------------------>

bool mmt_MP_engine::frameStarted( const Ogre::FrameEvent& evt )
{
	if(_isGameStarted)
	{
		try
		{
			Object::receiveMessage();
		}
		catch( boost::exception& e)
		{
		//	std::cout << e.<< std::endl;
			_isGameStarted = false;
			createGui();
			_ipInputLabel->show();
			if(VERBOSE_MODE > 0)
				VERBOSE_STRING("\n1. Wrong Server Ip \n2. Server not started jet.");
			
			return true;
		}

		if(VERBOSE_MODE > 3)
			VERBOSE_STRING("client is working - deltaTime:" << evt.timeSinceLastFrame);
		
		sendDataEveryFrame();
		handleCameraFollowing();
	}

	return BaseApplication::frameStarted( evt );
}

//------------------------------------------------------------>
void mmt_MP_engine::sendDataEveryFrame()
{
	if(_isGameStarted)
	{
		if(_controlMessage)
			Object::sendMessageNow( _controlMessage );
	} 
}
//------------------------------------------------------------>

bool mmt_MP_engine::frameRenderingQueued( const Ogre::FrameEvent& evt )
{
	return BaseApplication::frameRenderingQueued( evt );
}

//-------------------------------------------------------------------------------------
bool mmt_MP_engine::keyPressed( const OIS::KeyEvent& arg )
{
	keyPressedIpInputLabel(arg);

	switch(arg.key)
	{
		case OIS::KC_W:
			_controlMessage->moveZ = -1.0;
			break;
		case OIS::KC_S:
			_controlMessage->moveZ = 1.0;
			break;
		case OIS::KC_A:
			_controlMessage->moveX = -1.0;
			break;
		case OIS::KC_D:
			_controlMessage->moveX = 1.0;
			break;
		case OIS::KC_SPACE:
			_controlMessage->moveY = 1.0;
			break;
		case OIS::KC_LCONTROL:
			_controlMessage->moveY = -1.0;
			break;
	}	
	return BaseApplication::keyPressed( arg );
}

//------------------------------------------------------------>
bool mmt_MP_engine::keyReleased( const OIS::KeyEvent& arg )
{
	switch(arg.key)
	{
		case OIS::KC_W:
			_controlMessage->moveZ = 0.0;
			break;
		case OIS::KC_S:
			_controlMessage->moveZ = 0.0;
			break;
		case OIS::KC_A:
			_controlMessage->moveX = 0.0;
			break;
		case OIS::KC_D:
			_controlMessage->moveX = 0.0;
			break;
		case OIS::KC_SPACE:
			_controlMessage->moveY = 0.0;
			break;
		case OIS::KC_LCONTROL:
			_controlMessage->moveY = 0.0;
			break;
	}	
	return BaseApplication::keyReleased( arg);
}

//------------------------------------------------------------>
bool mmt_MP_engine::keyPressedIpInputLabel( const OIS::KeyEvent& arg )
{
	if(!_isGameStarted)
	{	
		//check if label is initiialized
		if(!_ipInputLabel)
			return true;

		// ENTER
		if(arg.key == 28)
		{
			connectToServer(_ipInputLabel->getCaption());
			mTrayMgr->destroyWidget(_ipInputLabel);
			_ipInputLabel = 0;
			return true;
		}

		// backspace - delete last element of ipInputLabel caption
		if(arg.key == 14)
		{
			std::string currentCaption = _ipInputLabel->getCaption();	
			currentCaption = currentCaption.substr (0,currentCaption.size()-1);
			_ipInputLabel->setCaption(currentCaption);
		}
		
		//escape if there are enough chars in label
		if(_ipInputLabel->getCaption().size() > IP_LABEL_MAXIMUM_CHARS)
			return true;

		//allow letters from a-z && A-Z and Numbers from 0-9, also allow .
		boost::regex e("[a-zA-Z0-9.]");
		char inputChar = arg.text;
		std::string inputCharAsString = "";
		inputCharAsString = inputCharAsString + inputChar;
		if(boost::regex_match(inputCharAsString,e))
		{
			std::string currentCaption = _ipInputLabel->getCaption();	
			_ipInputLabel->setCaption(currentCaption + inputChar);
		}
		
	}
	return true;
}


//------------------------------------------------------------>
bool mmt_MP_engine::mouseMoved( const OIS::MouseEvent &arg )
{
	if(_isGameStarted)
	{
		_controlMessage->mouseX += arg.state.X.rel;
		_controlMessage->mouseY += arg.state.Y.rel;
	}

	return BaseApplication::mouseMoved(arg);
}

//-------------------------------------------------------------------------------------
bool mmt_MP_engine::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
	if(_isGameStarted)
	{
		_controlMessage->fire = arg.state.buttonDown(OIS::MB_Left);
		//_controlMessage->rightPressed = arg.state.buttonDown(OIS::MB_Right);
	}
	return BaseApplication::mousePressed(arg,id);
}

//-------------------------------------------------------------------------------------
bool mmt_MP_engine::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
	if(_isGameStarted)
	{
		_controlMessage->fire = arg.state.buttonDown(OIS::MB_Left);
		//_controlMessage->rightPressed = arg.state.buttonDown(OIS::MB_Right);
	}
	return BaseApplication::mouseReleased(arg,id);
}

void mmt_MP_engine::onReceiveControlMessage(ControlMessage *receivedMessage,boost::asio::ip::udp::endpoint senderEndpoint)
{
	// not needed by client
}

//------------------------------------------------------------>
void mmt_MP_engine::onReceiveItemMessage(ItemMessage *receivedMessage,boost::asio::ip::udp::endpoint senderEndpoint)
{	
	// check for new elements
	ID id= receivedMessage->_id;

	MessageObjectNode* node = items[id];
	if(node == NULL)
	{
		node = new MessageObjectNode(id, "hovercraft.mesh");
		items[id] = node;
		node->sceneNode->setScale(Ogre::Vector3(0.1,0.1,0.1));
	}

	node->sceneNode->setPosition(receivedMessage->positionX, receivedMessage->positionY, receivedMessage->positionZ);
	node->sceneNode->setOrientation(Ogre::Quaternion(Ogre::Radian(receivedMessage->attitude),Ogre::Vector3(receivedMessage->fX,receivedMessage->fY,receivedMessage->fZ)));
}

//------------------------------------------------------------>
void mmt_MP_engine::onReceivePlayerMessage(PlayerMessage *receivedMessage,boost::asio::ip::udp::endpoint senderEndpoint)
{
	// check for new elements
	ID id= receivedMessage->_id;

	MessageObjectNode* node = players[id];
	if(node == NULL)
	{
		node = new MessageObjectNode(id, "hovercraft.mesh");
		players[id] = node;
		node->sceneNode->setScale(Ogre::Vector3(0.1,0.1,0.1));
	}

	node->sceneNode->setPosition(receivedMessage->positionX, receivedMessage->positionY, receivedMessage->positionZ);
	node->sceneNode->setOrientation(Ogre::Quaternion(Ogre::Radian(receivedMessage->attitude),Ogre::Vector3(receivedMessage->fX, receivedMessage->fY, receivedMessage->fZ)));

	//check if own player
	if(id.machineID != Object::getMachineID())
		return;

	//make new sceneNode for this Player
	if(!_myPlayer)
	{
		_myPlayer = node;
		_myPlayer->sceneNode->setScale(Ogre::Vector3(0.1,0.1,0.1));
	}
}

//------------------------------------------------------------>
void mmt_MP_engine::createGui()
{
	if(!_ipInputLabel)
		_ipInputLabel = mTrayMgr->createLabel(OgreBites::TL_CENTER,"ipLabel",IP_LABEL_DEFAULT_VALUE, IP_LABEL_WIDTH);
}
void mmt_MP_engine::createCoordinateSystem()
{
	Ogre::ManualObject* yCoordinate =  mSceneMgr->createManualObject("YCoordinate"); 
	Ogre::ManualObject* xCoordinate =  mSceneMgr->createManualObject("XCoordinate"); 
	Ogre::ManualObject* zCoordinate =  mSceneMgr->createManualObject("ZCoordinate"); 

	_coordinateSystemSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("CoordinateSystemSceneNode"); 
	 
	//make MATERIALS
	Ogre::MaterialPtr greenMaterial = Ogre::MaterialManager::getSingleton().create("greenMaterial","Popular"); 
	greenMaterial->setReceiveShadows(false); 
	greenMaterial->getTechnique(0)->setLightingEnabled(true); 
	greenMaterial->getTechnique(0)->getPass(0)->setDiffuse(0,1,0,0); 
	greenMaterial->getTechnique(0)->getPass(0)->setAmbient(0,1,0); 
	greenMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(0,1,0); 

	Ogre::MaterialPtr redMaterial = Ogre::MaterialManager::getSingleton().create("redMaterial","Popular"); 
	redMaterial->setReceiveShadows(false); 
	redMaterial->getTechnique(0)->setLightingEnabled(true); 
	redMaterial->getTechnique(0)->getPass(0)->setDiffuse(1,0,0,0); 
	redMaterial->getTechnique(0)->getPass(0)->setAmbient(1,0,0); 
	redMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(1,0,0); 

	Ogre::MaterialPtr blueMaterial = Ogre::MaterialManager::getSingleton().create("blueMaterial","Popular"); 
	blueMaterial->setReceiveShadows(false); 
	blueMaterial->getTechnique(0)->setLightingEnabled(true); 
	blueMaterial->getTechnique(0)->getPass(0)->setDiffuse(0,0,1,0); 
	blueMaterial->getTechnique(0)->getPass(0)->setAmbient(0,0,1); 
	blueMaterial->getTechnique(0)->getPass(0)->setSelfIllumination(0,0,1); 

	xCoordinate->begin("redMaterial", Ogre::RenderOperation::OT_LINE_LIST); 
		//arrow length
		xCoordinate->position(  0,  0,  0  ); 
		xCoordinate->position(  5,  0,  0  ); 
		
		//peak
		xCoordinate->position(  5,  0,  0  ); 
		xCoordinate->position(  4,  1,  0  ); 
		xCoordinate->position(  5,  0,  0  ); 
		xCoordinate->position(  4, -1,  0  );

		
		float xSize = 1.0;
		Ogre::Vector3 shiftVectorX = Ogre::Vector3(6,1,0);
		//draw X
		xCoordinate->position(  shiftVectorX.x + 0,			
								shiftVectorX.y + 0,			
								shiftVectorX.z + 0  ); 
		xCoordinate->position(  shiftVectorX.x + xSize ,	
								shiftVectorX.y + xSize,		
								shiftVectorX.z + 0  ); 

		xCoordinate->position(  shiftVectorX.x + 0,			
								shiftVectorX.y + xSize,		
								shiftVectorX.z + 0  ); 
		xCoordinate->position(  shiftVectorX.x + xSize,		
								shiftVectorX.y +0,			
								shiftVectorX.z + 0  ); 
		
	xCoordinate->end(); 

	yCoordinate->begin("greenMaterial", Ogre::RenderOperation::OT_LINE_LIST); 
		//arrow length	
		yCoordinate->position(  0,  0,  0  ); 
		yCoordinate->position(  0,  5,  0  ); 

		//peak
		yCoordinate->position(  0,  5,  0  ); 
		yCoordinate->position(  1,  4,  0  ); 
		yCoordinate->position(  0,  5,  0  ); 
		yCoordinate->position( -1,  4,  0  ); 

		float ySize = 1.0;
		float halfYSize = ySize / 2;
		Ogre::Vector3 shiftVectorY = Ogre::Vector3(-halfYSize,6,0);

		//draw y
		yCoordinate->position(  shiftVectorY.x + 0,				
								shiftVectorY.y + 0,				
								shiftVectorY.z + 0  ); 
		yCoordinate->position(  shiftVectorY.x + ySize,			
								shiftVectorY.y + ySize,			
								shiftVectorY.z + 0  ); 

		yCoordinate->position(  shiftVectorY.x + 0,				
								shiftVectorY.y + ySize,			
								shiftVectorY.z + 0  ); 
		yCoordinate->position(  shiftVectorY.x + halfYSize,		
								shiftVectorY.y + halfYSize,		
								shiftVectorY.z + 0  ); 

	yCoordinate->end(); 

	zCoordinate->begin("blueMaterial", Ogre::RenderOperation::OT_LINE_LIST); 
		//arrow length	
		zCoordinate->position(  0,  0,  0  ); 
		zCoordinate->position(  0,  0,  5  ); 
		
		//peak
		zCoordinate->position(  0,  0,  5  ); 
		zCoordinate->position(  0,  1,  4  );  
		zCoordinate->position(  0,  0,  5  ); 
		zCoordinate->position(  0, -1,  4  ); 
		
		float zSize = 1.0;
		
		Ogre::Vector3 shiftVectorZ = Ogre::Vector3(-zSize/2,1,6);

		//draw y
		zCoordinate->position(  shiftVectorZ.x + 0,				
								shiftVectorZ.y + zSize,			
								shiftVectorZ.z + 0  ); 
		zCoordinate->position(  shiftVectorZ.x + zSize,			
								shiftVectorZ.y + zSize,			
								shiftVectorZ.z + 0  ); 

		zCoordinate->position(  shiftVectorZ.x + 0,				
								shiftVectorZ.y + 0,				
								shiftVectorZ.z + 0  ); 
		zCoordinate->position(  shiftVectorZ.x + zSize,			
								shiftVectorZ.y + zSize,			
								shiftVectorZ.z + 0  ); 

		zCoordinate->position(  shiftVectorZ.x + 0,				
								shiftVectorZ.y + 0,				
								shiftVectorZ.z + 0  ); 
		zCoordinate->position(  shiftVectorZ.x + zSize,			
								shiftVectorZ.y + 0,				
								shiftVectorZ.z + 0  ); 
	zCoordinate->end(); 
	
	_coordinateSystemSceneNode->attachObject(xCoordinate);
	_coordinateSystemSceneNode->attachObject(yCoordinate);
	_coordinateSystemSceneNode->attachObject(zCoordinate);
	_coordinateSystemSceneNode->setVisible(false);
}
//------------------------------------------------------------>
void mmt_MP_engine::connectToServer(std::string serverIpAdress)
{
	_coordinateSystemSceneNode->setVisible(true);
	Object::init(false);
	Network::addReceiverEndpoint(Network::getEndpoint(serverIpAdress.c_str()));
	
	_controlMessage = Object::createControlMessage();
	Object::sendMessageNow(_controlMessage);

	Object::startReceiving();

	Object::setMessageHandler( this );

	_isGameStarted = true;
	// setMessage Handler to receieve Messages from Team Object
	Object::setMessageHandler( this );
	createLevel();
}
//------------------------------------------------------------>
void mmt_MP_engine::createLevel()
{
	mCamera->setPosition(20.0,20.0,20.0);
	mCamera->lookAt(Ogre::Vector3(0,0,0));
	_level = new Level();
}

void mmt_MP_engine::handleCameraFollowing()
{
	if(!_myPlayer)
		return;

	Ogre::Vector3 cameraOffsetWorld = Ogre::Vector3(0,1,3);
	Ogre::Vector3 cameraOffsetLocal =	  _myPlayer->getDirection() * cameraOffsetWorld.z
										+ _myPlayer->getUpVector() * cameraOffsetWorld.y;
										+ _myPlayer->getRightVector() * cameraOffsetWorld.x;
	Ogre::Vector3 newCameraPosition = _myPlayer->sceneNode->getPosition() + cameraOffsetLocal;

	mCamera->setPosition(newCameraPosition);
	mCamera->lookAt(_myPlayer->sceneNode->getPosition());

	
}